#include "PassThroughController.h"
#include "core/utils/LimelightUtils.h"
#include "core/utils/XComponentUtils.h"
#include <Limelight.h>
#include <numbers>

#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN  0x0
#define LOG_TAG     "TouchController"
#include <hilog/log.h>


namespace {
constexpr bool DEBUG = false;
}

PassThroughController::PassThroughController() {
}

PassThroughController::~PassThroughController() {
}

void PassThroughController::applyConfig(const std::unordered_map<std::string, std::string> &overrides) {
}

void PassThroughController::handle(OH_NativeXComponent *const comp, void *const win, const TouchEvent &touchEvent) {
    struct {
        uint64_t width;
        uint64_t height;
    } renderGeometry;
    XCOMP_CALL_OR_ELSE(GetXComponentSize(comp, win, &renderGeometry.width, &renderGeometry.height), return);
    if constexpr (DEBUG) {
        OH_LOG_DEBUG(LOG_APP,
            "Received touch event on xcomp %{public}dx%{public}d @ (%{public}.2f,%{public}.2f) "
            "size %{public}.2f force %{public}.2f with type %{public}d",
            renderGeometry.width, renderGeometry.height, touchEvent.x, touchEvent.y,
            touchEvent.size, touchEvent.force, touchEvent.type);
    }
    for (int p = 0; p < touchEvent.numPoints; p++) {
        const auto &point = touchEvent.touchPoints[p];
        if constexpr (DEBUG) {
            OH_LOG_DEBUG(LOG_APP,
                "Received touch event on xcomp %{public}dx%{public}d @ (%{public}.2f,%{public}.2f) "
                "size %{public}.2f force %{public}.2f of finger %{public}d type %{public}d",
                renderGeometry.width, renderGeometry.height, point.x, point.y,
                point.size, point.id, point.type);
        }
        // NOTE currently the size and force factors are constantly zero
        uint8_t eventType = LI_TOUCH_EVENT_CANCEL;
        switch (point.type) {
        case OH_NATIVEXCOMPONENT_DOWN: { eventType = LI_TOUCH_EVENT_DOWN; break; }
        case OH_NATIVEXCOMPONENT_UP: { eventType = LI_TOUCH_EVENT_UP; break; }
        case OH_NATIVEXCOMPONENT_MOVE: { eventType = LI_TOUCH_EVENT_MOVE; break; }
        case OH_NATIVEXCOMPONENT_CANCEL: { eventType = LI_TOUCH_EVENT_CANCEL; break; }
        default: {
            OH_LOG_WARN(LOG_APP, "Unrecognized touch event type %{public}d", point.type);
            continue;
        }
        }
        OH_NativeXComponent_TouchPointToolType toolType = OH_NATIVEXCOMPONENT_TOOL_TYPE_UNKNOWN;
        XCOMP_CALL_OR_ELSE(GetTouchPointToolType(comp, p, &toolType),);
        switch (toolType) {
        case OH_NATIVEXCOMPONENT_TOOL_TYPE_FINGER: {
            LI_CALL_OR_ELSE(SendTouchEvent(
                eventType, point.id,
                1. * point.x / renderGeometry.width,
                1. * point.y / renderGeometry.height,
                /*pressureOrDistance=*/point.force,
                /*contactAreaMajor=*/1. * point.size / renderGeometry.width,
                /*contactAreaMinor=*/1. * point.size / renderGeometry.width,
                /*rotation=*/0
            ), return);
            break;
        }
        case OH_NATIVEXCOMPONENT_TOOL_TYPE_RUBBER:
        case OH_NATIVEXCOMPONENT_TOOL_TYPE_PEN:
        case OH_NATIVEXCOMPONENT_TOOL_TYPE_PENCIL: {
            float tiltToX = 0, tiltToY = 0; // ohos reports the angle from Z to the specified axis in degree
            XCOMP_CALL_OR_ELSE(GetTouchPointTiltX(comp, p, &tiltToX),);
            XCOMP_CALL_OR_ELSE(GetTouchPointTiltY(comp, p, &tiltToY),);
            const float normX = std::sin(tiltToX / 180 * std::numbers::pi);
            const float normY = std::sin(tiltToY / 180 * std::numbers::pi);
            const float normZ = std::sqrt(std::abs(1. - normX * normX - normY * normY));
            const float tiltToZ = std::acos(normZ) / std::numbers::pi * 180;
            float rotFromY = std::atan2(normY, -normX);
            if (rotFromY < 0) {
                rotFromY += 360;
            }
            if constexpr (DEBUG) {
                OH_LOG_DEBUG(LOG_APP, "tilt wrt x %{public}.2f, wrt y %{public}.2f", tiltToX, tiltToY);
            }
            LI_CALL_OR_ELSE(SendPenEvent(
                eventType,
                /*toolType=*/[&] {
                    if constexpr (DEBUG) {
                        OH_LOG_DEBUG(LOG_APP, "pen tool type is %{public}d", toolType);
                    }
                    switch (toolType) {
                    case OH_NATIVEXCOMPONENT_TOOL_TYPE_RUBBER: {
                        return LI_TOOL_TYPE_ERASER;
                    }
                    default: {
                        return LI_TOOL_TYPE_PEN;
                    }
                    }
                }(),
                /*TODO penButtons=*/0,
                1. * point.x / renderGeometry.width,
                1. * point.y / renderGeometry.height,
                /*pressureOrDistance=*/touchEvent.force,    // range is [0,1) same between ohos and limelight
                /*contactAreaMajor=*/1. * point.size / renderGeometry.width,
                /*contactAreaMinor=*/1. * point.size / renderGeometry.width,
                /*TODO rotation=*/rotFromY,
                /*TODO tilt=*/tiltToZ
            ), return);
            break;
        }
        default: {
            continue;
        }
        }
    }
}
